<script setup lang="ts">
import { Preview, downloadFile, previewTypeArr } from '@/components/Preview'
import { http } from '@/utils/http'
import type { UploadFile, UploadRawFile, UploadRequestOptions } from 'element-plus'
import { nextTick, ref, watch } from 'vue'

import Delete from '@iconify-icons/ep/delete'
import Download from '@iconify-icons/ep/download'
import View from '@iconify-icons/ep/view'
import { Icon } from '@iconify/vue'

import { message } from '@/utils/message'
import { getFileSuffix } from './utils'

const props = withDefaults(
  defineProps<{
    /** 最大文件数上传限制 */
    limit: number
    /** 最大文件尺寸限制 */
    max: number
    /** 禁用上传 */
    disabled?: boolean
    /** 支持的文件类型，用于提示的场景，也基于扩展名进行文件格式校验，需要带上. 例如.jpg */
    suffix: string[]
    /** 支持的文件类型，也是实际能限制上传类型的属性，默认支持所有文件类型 */
    accept: string
  }>(),
  {
    limit: 20,
    max: 10,
    accept: '*',
    suffix: () => ['.doc', '.docx', '.pdf', '.jpg'],
  }
)

const emit = defineEmits(['success', 'remove'])
const finished = defineModel('finished', { type: Boolean })
const fileIds = defineModel('fileIds', { type: String })

let pendingFiles = 0
const uploadRef = ref()
const fileList = ref([] as any)
const showSizeExcessError = ref(false)

const getFileInfo = async (ossIds: string) => {
  finished.value = false
  const { data } = await http
    .get<{}, HttpResponse>(`/resource/oss/listByIds/${ossIds}`)
    .finally(() => {
      finished.value = true
    })

  fileList.value = data.map((d) => ({
    name: d.originalName,
    url: d.url,
    uid: d.ossId as unknown as number,
    ossId: d.ossId,
  }))
}

/**
 * 上传文件方法
 * @param options
 */
async function requestFile(options: UploadRequestOptions) {
  finished.value = false
  pendingFiles++

  const { file } = options
  const formData = new FormData()
  formData.append('file', file)

  const { data } = await http
    .post<{}, HttpResponse>(
      '/resource/oss/upload',
      {
        data: formData,
      },
      {
        headers: { 'Content-Type': 'multipart/form-data' },
      }
    )
    .finally(() => {
      pendingFiles--
      if (pendingFiles === 0) {
        finished.value = true
      }
    })

  fileList.value.push({
    name: data.fileName,
    url: data.url,
    size: options.file.size,
    ossId: data.ossId,
  })
  if (pendingFiles == 0) {
    fileIds.value = fileList.value.map((v) => v.ossId).join(',')
  }
  emit('success', data, file)
}

/** 预览逻辑 */
const previewFormVisible = ref(false)
const previewSource = ref('')
const previewName = ref('')

const handleRemove = (uploadFile) => {
  if (!uploadFile.status || (uploadFile.status && uploadFile.status == 'success')) {
    const index = fileList.value.findIndex((item) => item.url == uploadFile.url)
    fileList.value.splice(index, 1)
    nextTick(() => {
      emit('remove', uploadFile)
    })
  }
}

async function beforeUpload(rawFile: UploadRawFile) {
  let suf = rawFile.name.split('.').pop()
  suf = '.' + suf
  let flag = false
  props.suffix.forEach((v) => {
    if (suf == v) {
      flag = true
    }
  })
  if (!flag) {
    message('请上传正确格式的文件', { type: 'warning' })
    return false
  }
  if (rawFile.size > props.max * 1024000) {
    message(`单个文件不超过${props.max}M`, { type: 'error' })
    return false
  }
}

function onExceed() {
  message('超过文件数量限制', { type: 'warning' })
}

/**
 * 预览文件
 * @param fileData 文件数据
 */
const toPreview = (fileData: UploadFile) => {
  previewFormVisible.value = true
  previewSource.value = fileData.url
  previewName.value = fileData.name
}

const handlePreview = (uploadFile: UploadFile) => {
  if (previewTypeArr.indexOf(getFileSuffix(uploadFile.name)) > -1) {
    toPreview(uploadFile)
  } else {
    downloadFile({
      filePath: uploadFile.url,
      fileName: uploadFile.name,
    })
  }
}
const handleDownload = (uploadFile: UploadFile) => {
  downloadFile({
    filePath: uploadFile.url,
    fileName: uploadFile.name,
  })
}

watch(fileIds, (v) => {
  if (!v) return

  getFileInfo(v)
})
</script>

<template>
  <el-upload
    ref="uploadRef"
    action="#"
    multiple
    :accept="accept"
    :file-list="fileList"
    :before-upload="beforeUpload"
    :http-request="requestFile"
    :on-remove="handleRemove"
    :on-exceed="onExceed"
    :on-preview="handlePreview"
    :limit="limit"
    :disabled="disabled"
  >
    <el-button type="primary" :disabled="disabled">上传文件</el-button>
    <template #tip>
      <div class="el-upload__tip">
        <p>1.支持扩展名：{{ suffix.join(', ') }}</p>
        <p>2.大小要求：单个文件不超过{{ max }}M</p>
        <p>3.最多支持上传{{ limit }}个文件</p>
      </div>
      <div v-show="showSizeExcessError" class="el-upload__tip text-red">
        最多只能上传{{ limit }}个文件
      </div>
    </template>
    <template #file="{ file }">
      <!--v-if-->
      <div class="el-upload-list__item-info">
        <span class="el-upload-list__item-name">
          <i class="el-icon el-icon--document">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
              <path
                fill="currentColor"
                d="M832 384H576V128H192v768h640V384zm-26.496-64L640 154.496V320h165.504zM160 64h480l256 256v608a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V96a32 32 0 0 1 32-32zm160 448h384v64H320v-64zm0-192h160v64H320v-64zm0 384h384v64H320v-64z"
              />
            </svg>
          </i>
          <span class="el-upload-list__item-file-name">{{ file.name }}</span>
        </span>
        <!--v-if-->
      </div>
      <label class="el-upload-list__item-status-label">
        <i class="el-icon el-icon--upload-success el-icon--circle-check">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
            <path
              fill="currentColor"
              d="M512 896a384 384 0 1 0 0-768 384 384 0 0 0 0 768zm0 64a448 448 0 1 1 0-896 448 448 0 0 1 0 896z"
            />
            <path
              fill="currentColor"
              d="M745.344 361.344a32 32 0 0 1 45.312 45.312l-288 288a32 32 0 0 1-45.312 0l-160-160a32 32 0 1 1 45.312-45.312L480 626.752l265.344-265.408z"
            />
          </svg>
        </i>
      </label>
      <div
        class="file-operation absolute w-full h-full flex items-center justify-end top-0 left-0 bg-[#ffffff80]"
      >
        <i
          v-show="previewTypeArr.indexOf(getFileSuffix(file.name)) > -1"
          title="预览"
          class="cursor-pointer ml-4"
          @click="handlePreview(file)"
        >
          <Icon :icon="View" color="#307bf6" />
        </i>
        <i title="下载" class="cursor-pointer ml-4" @click="handleDownload(file)">
          <Icon :icon="Download" color="#307bf6" />
        </i>
        <i
          v-show="file.status == 'success' && !props.disabled"
          title="删除"
          class="cursor-pointer ml-4"
          @click="handleRemove(file)"
        >
          <Icon :icon="Delete" color="#f56c6c" />
        </i>
      </div>
    </template>
  </el-upload>
  <div v-show="previewFormVisible" class="fixed left-0 top-0 w-full h-full z-[1000]">
    <Preview
      v-model:visible="previewFormVisible"
      :view-height="90"
      :disabled="false"
      :filePath="previewSource"
      :fileName="previewName"
    />
  </div>
</template>

<style lang="scss" scoped>
.text-red {
  color: var(--el-color-danger);
}

.file-operation {
  display: none;
}

:deep(.el-upload-list__item:hover .file-operation) {
  display: flex;
}

:deep(.el-upload-list) {
  width: 480px;
}
</style>
